﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace WebTranslator {
	//Client - Master Server protocol - Server version
	public class CMSProtocolS : ServerProtocol {
		private static int nextPort = StartingApplicationPorts.ClientPortsFrom;
		private static int TranslationServerCount = StartingApplicationPorts.NumberOfTranslationServers;
		public static MasterListenerTranslation[] protocolArray = new MasterListenerTranslation[TranslationServerCount];
		public static int[] usersCount = new int[TranslationServerCount];
		public void StartTSListeners() {
			for (int i = 0; i < StartingApplicationPorts.NumberOfTranslationServers; ++i) {
				usersCount[i] = 0;
				protocolArray[i] = new MasterListenerTranslation(this);
				protocolArray[i].Connect(StartingApplicationPorts.ChosenAddress, StartingApplicationPorts.MainServerToTranslationServers + i);
				Console.WriteLine("Server at " + (StartingApplicationPorts.MainServerToTranslationServers + i));
			}
		}
		private static TcpListener NextListener {
			get {
				TcpListener tcpListener = new TcpListener(StartingApplicationPorts.ChosenAddress, nextPort);
				try {
					tcpListener.Start();
				} catch (SocketException) {
					nextPort++;
					if (nextPort > StartingApplicationPorts.ClientPortsTo)
						nextPort = StartingApplicationPorts.ClientPortsFrom;
					return NextListener;
				}
				nextPort++;
				return tcpListener;
			}
		}

		public override void Listen() {
			while (true) {
				AcceptSocket();
				if (Receive().StartsWith("czesc")) {
					TcpListener listener = NextListener;
					new Thread(new ClientConnection(listener).Run).Start();
					Send("czesc " + (listener.LocalEndpoint as IPEndPoint).Port);
				} else {
					Send("Unrecognized command.");
				}
			}
		}
		class ClientConnection : ServerProtocol {
			bool authenticated = false;
			internal ClientConnection(TcpListener listener) {
				tcpListener = listener;
			}
			internal void Run() {
				AcceptSocket();
				if (Receive() == "OK") {
					Send("OK");
					//		try {
					Listen();
					//} catch (Exception ex) {
					//					Disconnect();
					//					}
				} else {
					Send("Error. Connection closed.");
					return;
				}
			}
			//funkcja do obsługi połączenia z klientem
			public override void Listen() {
				string msg;
				string MozliweKomunikaty = "login" + " " + "rejestracja" + " " + "serwerjezykowy" + " " + "help" + " " + "quit";
				while (true) {
					msg = Receive();
					string[] napisy = msg.Split(' ');
					switch (napisy[0]) {
						case "login":
							if (napisy.Length < 3) {
								Send("unacceptable login/ pasword");
								break;
							}
							if (Authenticate(napisy[1], napisy[2])) {
								Send("OK " + napisy[1]);
							} else {
								Send("Bledny login/haslo");
							}
							break;
						case "serwerjezykowy":
							if (Authenticated()) {
								int maks = 5;
								int? goThere = null ;
								if (TranslationServerCount > 0) goThere = 1;
								for (int i = 0; i < TranslationServerCount; ++i) {
									if (maks < usersCount[i] && i > 0) {// jeśli wcześniej jest serwer z mniejszą liczbą osób
										goThere = i - 1; 
										break;
									} 
									if (usersCount[i] < 5 || usersCount[i] + 5 < maks) {
										goThere = i; 
										break;
									} else { maks = usersCount[i]; }
								}
								if (goThere == null)
									Send("Brak serwera");
								else {
									usersCount[goThere.Value]++;
									Send(protocolArray[goThere.Value].Ip + ":" + (protocolArray[goThere.Value].port + TranslationServerCount));
									return; //koniec komunikacji z tym klientem
								}
							} else {
								Send("Najpierw się zaloguj");
							}
							break;
						case "rejestracja":
							if (Authenticated()) {
								Send("Błąd! Już jesteś zarejestrowany");
							} else {
								if (Baza.JestUser(napisy[1]))
									Send("Wybierz inną nazwę użytkownika");
								else {
									Baza.DodajUser(napisy[1], napisy[2]);
									Send("OK");
								}
							}
							break;
						case "quit":
							Send("Connection closed");
							Disconnect();
							return;
						case "help":
							Send(MozliweKomunikaty);
							break;
						default:
							Send(MozliweKomunikaty);
							break;
					}
				}
			}

			private bool Authenticated() {
				return authenticated;
			}

			private bool Authenticate(string login, string password) {
				return authenticated = Baza.Logowanie(login, password);
			}
		}

		internal void StartListener() {
			StartTSListeners();
			StartListener(StartingApplicationPorts.MainServerToClients);
		}
	}
}
